-module(mylib).
-export([fib/1, factors/1, factor_set/1, is_prime/1,
         prime_list/2, tail/1, is_palindrome/1,
         is_divisible_by_all/2, multi_all/1,
         floor/1, ceiling/1, is_actual_integer/1,
         is_odd/1, is_even/1, proper_divisors/1]).

%% Fibonacci
fib(1) -> 1;
fib(2) -> 2;
fib(N) -> fib(N-1) + fib(N-2).

%% Factor
factors(N) -> factor(2, N, [1]).

factor(X, N, List) ->
    if
        X < N ->
            if
                N rem X =:= 0 -> factor(X, N div X, [X|List]);
                true -> factor(X+1, N, List)
            end;
        true ->
            [N|List]
    end.


factor_set(N) -> factor_set(2, N, [1]).

factor_set(X, N, List) ->
    case (X - N) of
        Neg when Neg < 0 ->
            case N rem X of
                0 ->
                    factor_set(X, N div X, push_factor(X, List));
                _ ->
                    factor_set(X+1, N, List)
            end;
        _ ->
            push_factor(N, List)
    end.

push_factor(Factor, []) -> [Factor];
push_factor(Factor, FactorList) ->
    [H|_] = FactorList,
    case Factor of
        H ->
            FactorList;
        _ ->
            [Factor|FactorList]
    end.

is_prime(X) when X<6 ->
    case X of
        1 -> false;
        2 -> true;
        3 -> true;
        4 -> false;
        5 -> true
    end;
is_prime(X) ->
    is_prime_helper(X, 2, floor(math:sqrt(X))).

is_prime_helper(N, F, To) ->
    case F =< To of
        false -> true;
        _ ->
            case N rem F of
                0 -> false;
                _ -> is_prime_helper(N, F+1, To)
        end
    end.

prime_list(From, To) ->
    lists:filter(fun is_prime/1, lists:seq(From, To)).

%% Use lists:last/1 instead
tail(List) ->
    [H|_] = lists:reverse(List),
    H.

cut_tail(List) ->
    [_|T] = lists:reverse(List),
    T.

is_palindrome([_|T]) when T =:= [] -> true;
is_palindrome([H|T]) ->
    Tail = tail(T),
    case H =:= Tail of
        true -> is_palindrome(cut_tail(T));
        false -> false
    end;
is_palindrome([]) -> true.


is_divisible_by_all(Num, [Divisor|Rest]) ->
    case Num rem Divisor of
        0 ->
            is_divisible_by_all(Num, Rest);
        _ ->
            false
    end;
is_divisible_by_all(_, []) -> true.

multi_all([H|T]) -> H * multi_all(T);
multi_all([]) -> 1.

floor(X) ->
    T = erlang:trunc(X),
    case (X-T) of
        Neg when Neg < 0 -> T-1;
        Pos when Pos > 0 -> T;
        _ -> T
    end.

ceiling(X) ->
    T = erlang:trunc(X),
    case (X-T) of
        Neg when Neg < 0 -> T;
        Pos when Pos > 0 -> T+1;
        _ -> T
    end.

is_actual_integer(X) ->
    case (floor(X) - ceiling(X)) of
        0 -> true;
        _ -> false
    end.

is_odd(X) ->
    X rem 2 =/= 0.

is_even(X) ->
    X rem 2 =:= 0.

proper_divisors(N) ->
    proper_divisors(N, 1, []).

proper_divisors(N, Cur, PD) ->
    case N of
        Cur -> PD;
        _ ->
            case N rem Cur of
                0 ->
                    proper_divisors(N, Cur+1, [Cur|PD]);
                _ ->
                    proper_divisors(N, Cur+1, PD)
            end
    end.



